using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize
{
    public unsafe partial struct LStreamReader
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadArrayField<T>(LSerializeContext* context, ref T[] value)
        {
            if (!ReadManagedRefId(context, ref value, out var refId))
            {
                ReadNextDepthBegin<T[]>(context);
                ReadArrayValue<T>(context, ref value, ref refId);
                ReadNextDepthEnd<T[]>(context);
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadArrayValue<T>(LSerializeContext* context, ref T[] value, ref long refId)
        {
            ReadValueBegin<T[]>(context, ref value, ref refId);
            var length = context->RefHeader->ValCount;
            bool isUnmanaged = !RuntimeHelpers.IsReferenceOrContainsReferences<T>();
            if (isUnmanaged)
            {
                value = UnsafeReadUnmanagedArray<T>();
            }
            else
            {
                value ??= new T[length];
                for (int i = 0; i < length; i++)
                    ReadManagedField(context, ref value[i]);
            }
            ReadValueEnd<T[]>(context, ref value); 
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadListField<T>(LSerializeContext* context, ref List<T> value)
        {
            if (!ReadManagedRefId(context, ref value, out var refId))
            {
                // var length = context->RefHeader->ValCount;
                // value ??= new List<T>(length);
                //
                // var refHeader = context->RefHeader;
            
                ReadNextDepthBegin<List<T>>(context);
                // context->ResetRefHeader(false, refHeader->RefId, refHeader->ValCount, refHeader->RefCount);
                // context->Total = context->Skip = length;
                // context->Head = Seek(0, SeekOrigin.Current);
                ReadListValue<T>(context, ref value, ref refId);
                ReadNextDepthEnd<List<T>>(context); 
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadListValue<T>(LSerializeContext* context, ref List<T> value, ref long refId)
        {
            ReadValueBegin<List<T>>(context, ref value, ref refId);
            var length = context->RefHeader->ValCount;
            
            value ??= new List<T>(length);
            bool isUnmanaged = !RuntimeHelpers.IsReferenceOrContainsReferences<T>();
            for (int i = 0; i < length; i++)
            {
                T val = default;
                if (isUnmanaged)
                    GetFormatter<T>().Deserialize(context, Ptr, ref val);
                else
                    ReadManagedField(context, ref val);
                value.Add(val);
            }
            
            ReadValueEnd<List<T>>(context, ref value);
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadDictionaryField<TKey, TValue>(LSerializeContext* context, ref Dictionary<TKey, TValue> value) where TKey : notnull
        {
            if (!ReadManagedRefId(context, ref value, out var refId))
            {
                ReadNextDepthBegin<Dictionary<TKey, TValue>>(context);
                ReadDictionaryValue<TKey, TValue>(context, ref value, ref refId);
                ReadNextDepthEnd<Dictionary<TKey, TValue>>(context);
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadDictionaryValue<TKey, TValue>(LSerializeContext* context, ref Dictionary<TKey, TValue> value, ref long refId)
            where TKey : notnull
        {
            ReadValueBegin<Dictionary<TKey, TValue>>(context, ref value, ref refId);
            var length = context->RefHeader->ValCount / 2;
            
            value ??= new Dictionary<TKey, TValue>(length);
            bool isUnmanaged = !RuntimeHelpers.IsReferenceOrContainsReferences<TKey>() &&
                               !RuntimeHelpers.IsReferenceOrContainsReferences<TValue>();
            
            for (int i = 0; i < length; i++)
            {
                TKey key = default;
                TValue val = default;
                if (isUnmanaged)
                {
                    GetFormatter<TKey>().Deserialize(context, Ptr, ref key);
                    GetFormatter<TValue>().Deserialize(context, Ptr, ref val); 
                }
                else
                {
                    ReadManagedField<TKey>(context, ref key);
                    ReadManagedField<TValue>(context, ref val);
                    
                }
                value.Add(key, val);
            }
            
            ReadValueEnd<Dictionary<TKey, TValue>>(context, ref value);
        }
        
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadArray<T>(LSerializeContext* context, ref T[] value) where T : IEquatable<T>
        {
            if (ReadBufferSpan(context, ref value, out var span))
            {
                var origin = buffer;
                buffer = span.Buffer;
                buffer->Seek(0, SeekOrigin.Begin);
                buffer->HandleUnmanagedStruct(out RefHeader* header);
                context->IncreaseDepth();
                var count = ReadUInt16();
                bool isUnmanaged = !RuntimeHelpers.IsReferenceOrContainsReferences<T>();
                if (isUnmanaged)
                {
                    value = UnsafeReadUnmanagedArray<T>(); 
                }
                else
                {
                    value ??= new T[count];
                    BuildBufferSpanCache();
                    for (int i = 0; i < count; i++)
                    { 
                        T v = default;
                        ReadRealTypeValue<T>(context, ref v);
                        value[i] = v;
                    } 
                }
                
                buffer = origin;
                context->DecreaseDepth();
                LSerializeExtension.CacheObj.CacheSerializeObj(header->InstId, ref value);
            }
        } 

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadList<T>(LSerializeContext* context, ref List<T> value) where T : IEquatable<T>
        {
            if (ReadBufferSpan(context, ref value, out var span))
            {
                var origin = buffer;
                buffer = span.Buffer;
                buffer->Seek(0, SeekOrigin.Begin);
                buffer->HandleUnmanagedStruct(out RefHeader* header);
                context->IncreaseDepth();
                var count = ReadUInt16();
                value ??= new List<T>(count);
                BuildBufferSpanCache();
                bool isUnmanaged = !RuntimeHelpers.IsReferenceOrContainsReferences<T>();
                for (int i = 0; i < count; i++)
                {
                    T v = default;
                    if (isUnmanaged)
                    {
                        UnsafeReadUnmanagedStruct(context, ref v);
                    }
                    else
                    {
                        ReadRealTypeValue<T>(context, ref v);
                    }
                    value.Add(v);
                }
                buffer = origin;
                context->DecreaseDepth();
                LSerializeExtension.CacheObj.CacheSerializeObj(header->InstId, ref value);
            }
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ReadDictionary<TKey, TValue>(LSerializeContext* context, ref Dictionary<TKey, TValue> value)
            where TKey : notnull, IEquatable<TKey> 
            where TValue : IEquatable<TValue>
        {
            if (ReadBufferSpan(context, ref value, out var span))
            {
                var origin = buffer;
                buffer = span.Buffer;
                buffer->Seek(0, SeekOrigin.Begin);
                buffer->HandleUnmanagedStruct(out RefHeader* header);
                context->IncreaseDepth();
                var count = ReadUInt16();
                value ??= new Dictionary<TKey, TValue>();
                BuildBufferSpanCache();
                bool isKeyUnmanaged = !RuntimeHelpers.IsReferenceOrContainsReferences<TKey>();
                bool isValueUnmanaged = !RuntimeHelpers.IsReferenceOrContainsReferences<TValue>();

                for (int i = 0; i < count; i++)
                {
                    TKey key = default;
                    if (isKeyUnmanaged)
                        UnsafeReadUnmanagedStruct(context, ref key);
                    else
                        ReadRealTypeValue<TKey>(context, ref key);
                    
                    TValue val = default;
                    if (isValueUnmanaged)
                        UnsafeReadUnmanagedStruct(context, ref val);
                    else
                        ReadRealTypeValue<TValue>(context, ref val);
                    value.Add(key, val);
                }
                buffer = origin;
                context->DecreaseDepth();
                LSerializeExtension.CacheObj.CacheSerializeObj(header->InstId, ref value);
            }
        } 
        
    }
}